use std::future::{ready, Ready};
use std::io;
use std::time::{SystemTime, UNIX_EPOCH};

use actix_web::{App, Error, HttpServer, middleware, web};
use actix_web::dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform};
use actix_web::middleware::Logger;
use env_logger::Env;
use futures::future::LocalBoxFuture;
use futures::TryFutureExt;
use futures_util::future::FutureExt;

// 中间件处理有两个步骤。
// 1. 中间件初始化，中间件工厂以链中的下一个服务为参数进行调用。
// 2. 中间件的调用方法通过正常请求进行调用。
pub struct SayHi;

// 中间件工厂有 `Transform` 特征
// `S` - 下一个服务的类型
// `B` - 响应的主体类型
impl<S, B> Transform<S, ServiceRequest> for SayHi
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Transform = SayHiMiddleware<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(SayHiMiddleware { service }))
    }
}

pub struct SayHiMiddleware<S> {
    service: S,
}

impl<S, B> Service<ServiceRequest> for SayHiMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    forward_ready!(service);

    fn call(&self, req: ServiceRequest) -> Self::Future {
        println!("Hi from start. You requested: {}", req.path());

        let fut = self.service.call(req);

        Box::pin(async move {
            let res = fut.await?;
            println!("Hi from response");
            Ok(res)
        })
    }
}


#[actix_web::main]
async fn main() -> io::Result<()> {

    use actix_web::{App, HttpServer};
    // env_logger::init_from_env(Env::default().default_filter_or("info"));
    env_logger::Builder::from_env(Env::default().default_filter_or("info"))
        .format(|buf,record|{
            use std::io::Write;
            let now = chrono::Local::now();
            writeln!(buf, "{} [{}] - {}(Line: {}) {}", now.format("%Y-%m-%dT%H:%M:%S%:z"), record.level(), record.target(),record.line().unwrap_or(0), record.args())
        })
        .init();

    HttpServer::new(|| {
        App::new()
            .wrap(Logger::default())
            .wrap(Logger::new("%a %{User-Agent}i"))
            .wrap_fn(|req, srv| {
                println!("t Hi from start. You requested: {}", req.path());
                srv.call(req).map(|res| {
                    println!("t Hi from response");
                    res
                })
            })
            // 这个中间件,就是spring mvc中的拦截器
            .wrap(SayHi)
            // 设置默认的响应头
            .wrap(middleware::DefaultHeaders::new().add(("X-Version","0.2")))
            .route(
                "/index.html",
                web::get().to(|| async { "Hello, middleware!" }),
            )
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}
